package com.fuyao.cloud.common.data.mybatis;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.Date;
import java.util.List;

/**
 * 条件构造器拼接条件。
 *
 * @author lyhil
 * @date 2021年12月22日
 */
public class SpliceQueryWrapper {

	private static String EMPTY_NULL = "null";

	/**
	 * 根据条件对象拼接构造器。
	 *
	 * @param searchDto 条件对象
	 */
	@SneakyThrows
	public static QueryWrapper spliceSearchCondition(Object searchDto) {
		QueryWrapper queryWrapper = new QueryWrapper();
		Field[] declaredFields = searchDto.getClass().getDeclaredFields();
		for (Field field : declaredFields) {
			SearchCondition annotation = field.getAnnotation(SearchCondition.class);
			if(ObjectUtil.isNull(annotation)){
				continue;
			}
			String op = annotation.op();
			String mappingField = annotation.mappingField();
			boolean ignore = annotation.ignore();
			// 跳过ignore = true
			if (ignore) {
				continue;
			}
			// 判断是否有映射字段。
			if (StrUtil.isEmpty(mappingField)) {
				mappingField = field.getName();
			}
			field.setAccessible(Boolean.TRUE);
			Object value = field.get(searchDto);
			if (ObjectUtils.isNotEmpty(value) && !EMPTY_NULL.equals(value)) {
				switchOp(field, op, value, StrUtil.toUnderlineCase(mappingField), queryWrapper);
			}
		}
		return queryWrapper;
	}

	@SneakyThrows
	private static Object getValueType(Field field, Object value) {
		if (Integer.class.equals(field.getType())) {
			return Integer.parseInt(value.toString());
		}
		if (Long.class.equals(field.getType())) {
			return Long.parseLong(value.toString());
		}
		if (Short.class.equals(field.getType())) {
			return Short.parseShort(value.toString());
		}
		if (String.class.equals(field.getType())) {
			return String.valueOf(value);
		}
		if (Date.class.equals(field.getType())) {
			return DateUtil.parse(value.toString());
		}
		if (LocalDateTime.class.equals(field.getType())) {
			return LocalDateTimeUtil.parse(value.toString());
		}
		if (LocalDate.class.equals(field.getType())) {
			return LocalDateTimeUtil.parse(value.toString(), "yyyy-MM-dd");
		}
		if (LocalDate[].class.equals(field.getType())) {
			return value;
		}
		if (LocalDateTime[].class.equals(field.getType())) {
			return value;
		}
		if (List.class.equals(field.getType())) {
			return value;
		}
		if (BigDecimal.class.equals(field.getType())) {
			return value;
		}
		return null;
	}

	private static void switchOp(Field field, String op, Object valueType, String mappingFiled, QueryWrapper queryWrapper) {
		boolean hasCondition = false;
		Object value = getValueType(field, valueType);
		if (ObjectUtils.isNotEmpty(value) && !EMPTY_NULL.equals(value)) {
			hasCondition = true;
		}
		switch (op) {
			case SearchOp.EQ:
				AddSearchCondition.eq(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.GT:
				AddSearchCondition.gt(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.LT:
				AddSearchCondition.lt(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.GE:
				AddSearchCondition.ge(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.LE:
				AddSearchCondition.le(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.LIKE:
				AddSearchCondition.like(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.LEFT_LIKE:
				AddSearchCondition.likeLeft(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.RIGHT_LIKE:
				AddSearchCondition.likeRight(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.NOT_LIKE:
				AddSearchCondition.notLike(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.IN:
				AddSearchCondition.in(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.NOT_IN:
				AddSearchCondition.notIn(queryWrapper, hasCondition, mappingFiled, value);
				break;
			case SearchOp.BETWEEN:
				AddSearchCondition.between(queryWrapper, hasCondition, mappingFiled, value);
				break;
			default:
				break;
		}
	}
}
